#pragma once
#ifndef TM_GPU_CIRATEFI_HELPER_H_
#define TM_GPU_CIRATEFI_HELPER_H_

namespace recognition {

typedef struct FirstGradePoint {
	__host__ __device__ FirstGradePoint(Point2D _p = Point2D(), int _scale_idx = 0) :
			p(_p), scale_idx(_scale_idx) {}
	Point2D p;
	int scale_idx;
} FirstGradePoint;

typedef struct SecondGradePoint {
	__host__ __device__ SecondGradePoint(Point2D _p = Point2D(), int _scale_idx = 0, int _angle_idx = 0) :
			p(_p), scale_idx(_scale_idx), angle_idx(_angle_idx) {}
	Point2D p;
	int scale_idx;
	int angle_idx;
} SecondGradePoint;

typedef struct CiratefiBoundaries {
	__host__ __device__ CiratefiBoundaries() : s(BoundsWithStep<double>()), r(BoundsWithStep<int>()), phi(BoundsWithStep<double>()),
			angle_incr(0), line_incr(0) {}
	BoundsWithStep<double> s;
	BoundsWithStep<int> r;
	BoundsWithStep<double> phi;
	// Circular step in Cis calculations
	int angle_incr;
	// Linear step in Ras calculations
	int line_incr;
} CiratefiBoundaries;

typedef struct BCThresholds {
	__host__ __device__ BCThresholds(double _beta = 0.1, double _gamma = 1) : beta(_beta), gamma(_gamma) {}
	double beta, gamma;
} BCThresholds;

typedef struct CiratefiThresholds {
	__host__ __device__ CiratefiThresholds(double _cifi_t1 = 0, double _rafi_t2 = 0, double _tefi_t3 = 0) :
			cifi_t1(_cifi_t1), rafi_t2(_rafi_t2), tefi_t3(_tefi_t3) {}
	double cifi_t1, rafi_t2, tefi_t3;
} CiratefiThresholds;

typedef struct CiratefiResult {
	CiratefiResult(std::vector<SecondGradePoint> _points = std::vector<SecondGradePoint>(),
			SecondGradePoint _best_match = SecondGradePoint(), double _max_corr = 0) :
				points(_points), best_match(_best_match), max_corr(_max_corr) {}
	std::vector<SecondGradePoint> points;
	SecondGradePoint best_match;
	double max_corr;
} CiratefiResult;

typedef enum CorrelationSchemeType {
	// H. Y. Kim and S. A. Araújo, "Grayscale Template-Matching Invariant to Rotation, Scale, Translation, Brightness and Contrast,"
	BCinvariance2007,

	BCinvarinace2009
} CorrelationSchemeType;

typedef struct Point2D_lst {
	Point2D* points;
	int size;
} Point2D_lst;

typedef struct SizeAndIdxs {
	__host__ __device__ SizeAndIdxs(int _size = 0, int _start_idx = 0, int _end_idx = 0) : size(_size), start_idx(_start_idx), end_idx(_end_idx) {}
	int size;
	int start_idx, end_idx;
} SizeAndIdxs;

typedef struct Line_lst {
	Point2D* points;
	SizeAndIdxs* size_and_idxs;
} Line_lst;

typedef struct _cu_create_C_A__shared__KernelArgs {
	_cu_create_C_A__shared__KernelArgs(CiratefiBoundaries _bounds, int _l, int _offset, int _num_blocks):
		bounds(_bounds), l(_l), offset(_offset), num_blocks(_num_blocks) {}

	CiratefiBoundaries bounds;
	int l;
	int offset;
	int num_blocks;
} _cu_create_C_A__shared__KernelArgs;

typedef struct _cu_create_C_A__global__KernelArgs {
	_cu_create_C_A__global__KernelArgs(CiratefiBoundaries _bounds, int _l, int _offset):
		bounds(_bounds), l(_l), offset(_offset) {}

	CiratefiBoundaries bounds;
	int l;
	int offset;
} _cu_create_C_A__global__KernelArgs;

typedef struct _cu_CisCorr_AQ__global__KernelArgs {
	_cu_CisCorr_AQ__global__KernelArgs(CiratefiThresholds _thresholds, BCThresholds _bc_thresholds, int _l, int _n):
		thresholds(_thresholds), bc_thresholds(_bc_thresholds), l(_l), n(_n) {}

	CiratefiThresholds thresholds;
	BCThresholds bc_thresholds;
	int l;
	int n;
} _cu_CisCorr_AQ__global__KernelArgs;

typedef struct _cu_create_R_A__global__KernelArgs {
	_cu_create_R_A__global__KernelArgs(CiratefiBoundaries _bounds, int _m, int _offset):
		bounds(_bounds), m(_m), offset(_offset) {}

	CiratefiBoundaries bounds;
	int m;
	int offset;
} _cu_create_R_A__global__KernelArgs;

typedef struct FirstGradeMatrix {
	FirstGradeMatrix(int _height = 0, int _width = 0) : height(_height), width(_width), scale_idxs(NULL) {}

	int width, height;
	int* scale_idxs;
} FirstGradeMatrix;

typedef struct DArrayWrap {
	double* elements;
} DArrayWrap;

typedef struct _cu_RasCorr_AQ__global__KernelArgs {
	_cu_RasCorr_AQ__global__KernelArgs(CiratefiThresholds _thresholds, BCThresholds _bc_thresholds, int _m):
		thresholds(_thresholds), bc_thresholds(_bc_thresholds), m(_m) {}

	CiratefiThresholds thresholds;
	BCThresholds bc_thresholds;
	int m;
} _cu_RasCorr_AQ__global__KernelArgs;

typedef struct _cu_eval_Tefi__global__KernelArgs {
	_cu_eval_Tefi__global__KernelArgs(CiratefiThresholds _thresholds, BCThresholds _bc_thresholds, CiratefiBoundaries _bounds,
			int _offset, int _w1, int _h1, double _Q_mean, double _sum_Q_centered_squared):
		thresholds(_thresholds), bc_thresholds(_bc_thresholds), bounds(_bounds),
		offset(_offset), w1(_w1), h1(_h1), Q_mean(_Q_mean), sum_Q_centered_squared(_sum_Q_centered_squared) {}

	CiratefiThresholds thresholds;
	BCThresholds bc_thresholds;
	CiratefiBoundaries bounds;
	int offset;
	int w1, h1;
	double Q_mean, sum_Q_centered_squared;
} _cu_Tefi__KernelArgs;

__host__ __device__ int RadiusToIdx(int radius, CiratefiBoundaries bounds);
__host__ __device__ int IdxToRadius(int idx, CiratefiBoundaries bounds);

__global__ void _cu_create_C_A__global__(Matrix2d* A, Point2D_lst* circles, _cu_create_C_A__global__KernelArgs* args, Matrix2d* C_A);
__global__ void _cu_create_C_A__shared__(Matrix2d* A, Point2D* circles, SizeAndIdxs* circles_offsets, SizeAndIdxs* circle_blocks,
			_cu_create_C_A__shared__KernelArgs* args, Matrix2d* C_A);

__host__ __device__ double corr(double* x, double* y, int npoints, BCThresholds bc_thresholds);

__global__ void _cu_CisCorr_AQ__global__(Matrix2d* CQ, Matrix2d* CA, _cu_CisCorr_AQ__global__KernelArgs* args, FirstGradeMatrix* CisCorr_mat);
__global__ void _cu_CisCorr_AQ__shared__(Matrix2d* CQ, Matrix2d* CA, _cu_CisCorr_AQ__global__KernelArgs* args, FirstGradeMatrix* CisCorr_mat);

__host__ __device__ int AngleToIdx(double phi, CiratefiBoundaries bounds);
__host__ __device__ double IdxToAngle(int idx, CiratefiBoundaries bounds);

__global__ void _cu_create_R_A__global__(Matrix2d* A, FirstGradePoint* fgps, int fgps_size, Line_lst* lines,
		_cu_create_R_A__global__KernelArgs *args, double* R_A);

__global__ void _cu_RasCorr_AQ__global__(DArrayWrap* RQjs, double* R_A, int fgps_size,
		_cu_RasCorr_AQ__global__KernelArgs *args, int* angle_idxs);

__host__ __device__ int ScaleToIdx(double scale, CiratefiBoundaries bounds);
__host__ __device__ double IdxToScale(int idx, CiratefiBoundaries bounds);

__global__ void _cu_eval_Tefi__global__(double* Q_centered, Matrix2d* A, SecondGradePoint* sgps, int sgps_size,
		_cu_eval_Tefi__global__KernelArgs *args, double* correlations);

// Evaluates coordinates of points on circle by reflecting point with given
// coordinates \p x, \p y over X and Y axes.
//
// \param x			- x coordinate
// \param y			- y coordinate
// \param circle	- points on circle
void CirclePoints(int x, int y, std::vector<Point2D>& circle);
// Evaluates coordinates of points on circle with center in (0, 0), \p radius
// and step \p angle_incr.
//
// \param radius		- radius of circle
// \param angle_incr	- step size
// \return	points on circle
std::vector<Point2D> MidpointCircle(int radius, int angle_incr);
// Evaluates coordinates of points on segment with start at point \p x0, \p y0,
// end at point \p x1, \p y1, with step \p line_incr.
//
// \param x0			- x coordinate of start point
// \param y0			- y coordinate of start point
// \param x1			- x coordinate of end point
// \param y1			- y coordinate of end point
// \param line_incr	- step size
// \return	points on segment
std::vector<Point2D> MidpointLine(int x0, int y0, int x1, int y1, int line_incr);

void ReportCiratefiResult(log4cxx::LoggerPtr logger, CiratefiBoundaries bounds, CiratefiResult res, double max_corr);

}	// namespace recognition

#define SWAP_INTS(x,y)   do { int tmp = x; x = y; y = tmp; } while (0)
#endif	// TM_GPU_CIRATEFI_HELPER_H_
